<?php defined('DM_PATH') or die('DIRECT ACCESS IS NOT ALLOWED');

class DM_Tree_Types {

    public static function add($typeName, $typeDesc, $fields, $allowedChildren, $allowedParents, $module = 'default', $generateClasses = true)
	{
		$typesTable = new DM_Tree_Types_Model();
	
		if (!preg_match('/[a-z0-9]+/s', $typeName))
		{
			throw new Exception('Wrong type name: '.$typeName);
		}
		
		if (!is_array($fields) || empty($fields))
		{
			throw new Exception('Cannot create empty type: '.$typeName);
		}
		
		if (!is_array($allowedChildren))
		{
			throw new Exception('Childrens must be array: '.$typeName);
		}
		
		if (!is_array($allowedParents))
		{
			throw new Exception('Parents must be array: '.$typeName);
		}
		
		if (!is_dir(APPLICATION_PATH.'/'.$module))
		{
			throw new Exception('Wrong module directory: '.$typeName);
		}
		
		$pageExists = $typesTable->count(
			DM_Db_Select::create()
				->where('`type_name` = "'.$typeName.'"')
		);
		
		if ($pageExists > 0)
		{
			throw new Exception('Current type already exists: '.$typeName);
		}
		
		$time = time();
		
		$insertIntoTypesData = [
			'type_name'     => $typeName,
			'type_desc'     => $typeDesc,
			'type_fields'   => serialize($fields),
			'types_module'  => $module,
			'type_added'    => $time,
			'type_modified' => $time,
		];
		
		$insertId = $typesTable->save($insertIntoTypesData);
		
		$newTableName = 'type_'.$typeName;
		
		$sql = 'CREATE TABLE IF NOT EXISTS`'.$newTableName.'` (
			`'.$newTableName.'_id` INT NOT NULL ,';
			
			foreach ($fields as $fieldName => $fieldParams)
			{
				$sql .= '`'.$newTableName.'_'.$fieldName.'` '.self::setType($fieldParams['type']).' CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,';
			}
		
		$sql .= 'PRIMARY KEY (  `'.$newTableName.'_id` )
		) ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci;';
		
		$query = new Dm_Db_Query;
		$query->q($sql);
		
		if ($generateClasses)
		{
			self::generateModel($typeName, $module);
			self::generateController($typeName, $module, $allowedChildren, $allowedParents, $fields);
		}
	}
	
	public static function delete($typeName)
	{
		$typesTable = new DM_Tree_Types_Model();
	
		$typesTable->select()->where('`type_name` = "'.$typeName.'"')->remove();
		
		$query = new Dm_Db_Query();
		$query->q('DROP TABLE IF EXISTS `type_'.$typeName.'`');
	}
	
	public static function update($typeName, $desc, $fields)
	{
		$typesTable = new DM_Tree_Types_Model();
		
		$typesTable->update([
				'type_desc' => $desc,
				'type_fields' => serialize($fields),
				'type_modified' => time(),
			], '`type_name` = "'.$typeName.'"');
			
		$query = new Dm_Db_Query();
		$columns = $query->q('SHOW COLUMNS FROM `type_'.$typeName.'`');
		
		$rightFields = array();
		$usedFields = array(
			0 => 'type_'.$typeName.'_id'
		);
		
		$i = 0;
		foreach ($fields as $key => $value)
		{
			$rightFields[$i] = 'type_'.$typeName.'_'.$key;
			$rightFieldParams[$i] = $value;
			
			$i++;
		}
		
		foreach($columns as $key => $column)
		{
			$columns[$key] = $column->toArray();
			
			if (!in_array($columns[$key]['Field'], $rightFields))
			{
				$query->q('ALTER TABLE `type_'.$typeName.'` DROP `'.$columns[$key]['Field'].'`');
			}
			else
			{
				$usedFields[] = $columns[$key]['Field'];
			}
		}
		
		foreach ($rightFields as $key => $value)
		{
			if (!in_array($value, $usedFields))
			{
				$usedFieldsValues = $usedFields;
			
				$query->q('ALTER TABLE `type_'.$typeName.'` ADD `'.$value.'` '.self::setType($rightFieldParams[$key]['type']).' CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL AFTER `'.end($usedFieldsValues).'`');
				$usedFields[] = 'type_'.$typeName.'_'.$value;
			}
		}
		
		return true;
	}
	
	private static function generateModel($typeName, $module)
	{
		$code  = '<?php defined(\'DM_PATH\') or die(\'DIRECT ACCESS IS NOT ALLOWED\');'."\n\n";
		
		$code .= 'class '.ucfirst($module).'_Model_'.ucfirst($typeName).' extends Model {'."\n";
			$code .= "\t".'public $name = \'type_'.$typeName.'\';'."\n";
			$code .= "\t".'public $primary = \'type_'.$typeName.'_id\';'."\n";
			$code .= "\t".'public $foreign = ['."\n";
				$code .= "\t\t".'\'DM_Tree_Model\' => ['."\n";
					$code .= "\t\t\t".'\'type_'.$typeName.'_id\' => ['."\n";
						$code .= "\t\t\t\t".'\'key\' => \'tree_id\','."\n";
						$code .= "\t\t\t\t".'\'type\' => DM_LINKTYPE_ONE_ONE,'."\n";
						$code .= "\t\t\t\t".'\'delete\' => \'cascade\','."\n";
						$code .= "\t\t\t\t".'\'update\' => \'none\','."\n";
					$code .= "\t\t\t".']'."\n";
				$code .= "\t\t".']'."\n";
			$code .= "\t".'];'."\n";
		$code .= '}'."\n";
		
		if (!is_dir(APPLICATION_PATH.'/'.$module.'/model'))
		{
			throw new Exception('Model directory for new type not exists: '.$typeName.' - '.$module);
		}
		
		$f = @fopen(APPLICATION_PATH.'/'.$module.'/model/'.$typeName.'.php', 'w');
		fwrite($f, $code);
		fclose($f);
	}
	
	private static function addQuotes($value)
	{
		return '\''.$value.'\'';
	}
	
	private static function generateController($typeName, $module, $allowedChildren, $allowedParents, $fields)
	{
		$code  = '<?php defined(\'DM_PATH\') or die(\'DIRECT ACCESS IS NOT ALLOWED\');'."\n\n";
		
		$code .= 'class '.ucfirst($module).'_Controller_'.ucfirst($typeName).' extends Controller {'."\n\n";
			$code .= "\t".'/* {public} */'."\n";
			$code .= "\t".'public $layout = \'layout\';'."\n\n";
			
			$code .= "\t".'/* {private} */'."\n";
			$code .= "\t".'private $type'.ucfirst($typeName).'Table;'."\n";
			$code .= "\t".'private $allowedChildren = array('.implode(', ', array_map('self::addQuotes', $allowedChildren)).');'."\n";
			$code .= "\t".'private $allowedParents = array('.implode(', ', array_map('self::addQuotes', $allowedParents)).');'."\n";
			$code .= "\t".'private $fields = array('.implode(', ', array_map('self::addQuotes', array_keys($fields))).');'."\n\n";
			
			$code .= "\t".'public function onInit()'."\n";
			$code .= "\t".'{'."\n";
				$code .= "\t\t".'$this->type'.ucfirst($typeName).'Table = new '.ucfirst($module).'_Model_'.ucfirst($typeName).'();'."\n\n";
			$code .= "\t".'}'."\n\n";
			
			$code .= "\t".'/* {actions} */'."\n";
			$code .= "\t".'public function indexAction()'."\n";
			$code .= "\t".'{'."\n";
				$code .= "\t\t".'$this->showAction();'."\n";
			$code .= "\t".'}'."\n\n";
			
			$code .= "\t".'public function showAction()'."\n";
			$code .= "\t".'{'."\n";
				$code .= "\t\t".'if ($this->getParam(\'link\'))'."\n";
				$code .= "\t\t".'{'."\n";
					$code .= "\t\t\t".'$result = $this->type'.ucfirst($typeName).'Table->select()->where(\'`tree_link` = \'.$this->getParam(\'link\'))->fetchRow()->toArray();'."\n";
					$code .= "\t\t\t".'$this->render(\'type_'.$typeName.'_item\');'."\n";
				$code .= "\t\t".'}'."\n";
				$code .= "\t\t".'else'."\n";
				$code .= "\t\t".'{'."\n";
					$code .= "\t\t\t".'$result = $this->type'.ucfirst($typeName).'Table->select()->fetchArray();'."\n";
					$code .= "\t\t\t".'$this->render(\'type_'.$typeName.'\');'."\n";
				$code .= "\t\t".'}'."\n";
			$code .= "\t".'}'."\n\n";
			
			$code .= "\t".'public function createAction()'."\n";
			$code .= "\t".'{'."\n";
				$code .= "\t\t".'$valuesToAdd = array();'."\n";
				$code .= "\t\t".'if (isset($_POST) && !empty($_POST))'."\n";
				$code .= "\t\t".'{'."\n";
					$code .= "\t\t\t".'foreach ($_POST as $key => $value)'."\n";
					$code .= "\t\t\t".'{'."\n";
						$code .= "\t\t\t\t".'if (in_array($key, $this->fields))'."\n";
						$code .= "\t\t\t\t".'{'."\n";
							$code .= "\t\t\t\t\t".'$valuesToAdd[$key] = $value;'."\n";
						$code .= "\t\t\t\t".'}'."\n";
					$code .= "\t\t\t".'}'."\n\n";
					
					$code .= "\t\t\t".'$insertId = $this->type'.ucfirst($typeName).'Table->save($valuesToAdd);'."\n";
				$code .= "\t\t".'}'."\n";
			$code .= "\t".'}'."\n\n";
			
			$code .= "\t".'public function updateAction()'."\n";
			$code .= "\t".'{'."\n";
				$code .= "\t\t".'$valuesToUpdate = array();'."\n";
				$code .= "\t\t".'if (isset($_POST) && !empty($_POST) && $this->getParam(0))'."\n";
				$code .= "\t\t".'{'."\n";
					$code .= "\t\t\t".'foreach ($_POST as $key => $value)'."\n";
					$code .= "\t\t\t".'{'."\n";
						$code .= "\t\t\t\t".'if (in_array($key, $this->fields))'."\n";
						$code .= "\t\t\t\t".'{'."\n";
							$code .= "\t\t\t\t".'$valuesToUpdate[$key] = $value;'."\n";
						$code .= "\t\t\t\t".'}'."\n";
					$code .= "\t\t\t".'}'."\n\n";
					
					$code .= "\t\t\t".'$insertId = $this->type'.ucfirst($typeName).'Table->update($valuesToUpdate, \'`tree_id`= \'.$this->getParam(0).\'\');'."\n";
				$code .= "\t\t".'}'."\n";
			$code .= "\t".'}'."\n\n";
			
			$code .= "\t".'public function deleteAction()'."\n";
			$code .= "\t".'{'."\n";
				$code .= "\t\t".'if ($this->getParam(0))'."\n";
				$code .= "\t\t".'{'."\n";
					$code .= "\t\t\t".'$this->type'.ucfirst($typeName).'Table->select()->where(\'`tree_id` = \'.$this->getParam(0))->remove();'."\n";
				$code .= "\t\t".'}'."\n";
			$code .= "\t".'}'."\n\n";
		$code .= '}'."\n";
		
		if (!is_dir(APPLICATION_PATH.'/'.$module.'/controller'))
		{
			throw new Exception('Controller directory for new type not exists: '.$typeName.' - '.$module);
		}
		
		$f = @fopen(APPLICATION_PATH.'/'.$module.'/controller/'.$typeName.'.php', 'w');
		fwrite($f, $code);
		fclose($f);
	}
	
	private static function setType($type)
	{
		switch ($type)
		{
			case 'text':
			case 'wysiwyg':
			case 'select':
			case 'option':
			case 'checkbox':
				return 'TEXT';
			case 'int':
			case 'string':
				return 'VARCHAR( 500 )';
			case 'date':
				return 'DATE';
			case 'datetime':
				return 'DATETIME';
			default: throw new Exception('Wrong type for new type field: '.$type);
		}
	}
}